import base64
import paho.mqtt.client as mqtt
import configparser
import re
import logging
import os

_mqtt_server = "aahqtdc.iot.gz.baidubce.com"
_mqtt_username = None
_mqtt_password = None
_mqtt_client = None
_config_file = "canary.ini"
_target_dev = None


red_ascii = "\033[31m"
reset = "\033[0m"
green = "\033[32m"
yellow = "\033[33m"


def prepare():
    global _mqtt_username, _mqtt_password
    if not os.path.exists(_config_file):
        raise Exception("config file not found")
    config = configparser.ConfigParser()
    config.read(_config_file)
    logging.basicConfig(
        level=logging.DEBUG,
        format="%(asctime)s %(filename)s/%(funcName)s/%(lineno)d [%(levelname)s]: %(message)s",
        datefmt="%Y/%m/%d %H:%M:%S",
    )
    _mqtt_username = config["canary"]["mqtt_username"]
    assert _mqtt_username
    _mqtt_username = f"thingidp@aahqtdc|{_mqtt_username}|0|MD5"
    _mqtt_password = config["canary"]["mqtt_password"]
    assert _mqtt_password
    _mqtt_password = base64.b64decode(_mqtt_password).decode("utf-8")


def on_connect(client, userdata, flags, rc):
    if rc:
        raise Exception("connect failed")


def on_disconnect(client, userdata, flags):
    logging.info("mqtt connection lost")


def publish_msg(msg, topic=None):
    assert _mqtt_client
    assert isinstance(msg, str) or isinstance(msg, bytes)
    if not topic:
        assert _target_dev
        topic = f"lily/{_target_dev}/msg"
    else:
        assert isinstance(topic, str)
    _mqtt_client.publish(topic, msg)
    print(f"{yellow}@{topic}:{reset}{msg}")


def on_message(client_name, userdata, msg):
    print(f"{green}{msg.topic}{reset}")
    print(f'{msg.payload.decode("utf-8")}\n')


def get_input():
    last_topic = None
    while True:
        try:
            cmd = input(">>").strip()
            if not cmd:
                continue
            if cmd == "exit":
                break
            if not _mqtt_client:
                raise Exception("no mqtt client")
            if cmd == "help":
                print("commands: exit, help, set_dev, pub, sub and unsub")
                continue
            if cmd == "set_dev":
                dev = input("input target device: ")
                if not re.match(r"^\w+$", dev):
                    raise Exception("invalid device name")
                global _target_dev
                _target_dev = dev
                print(f"target device set to {dev}")
                continue
            if cmd == "pub":
                topic = input("topic: ")
                if topic and not re.match(r"^lily/[\w+#]+(/[\w+#]+)*$", topic):
                    topic = f"lily/{_target_dev}/cmd/{topic}"
                    print(f"topic set to {topic}")
                elif not topic:
                    topic = last_topic
                    print(f"topic set to {topic}")
                msg = input("message: ")
                publish_msg(msg, topic)
                last_topic = topic
                continue
            if cmd == "sub":
                topic = input("topic: ")
                if not re.match(r"^\w+/[\w+#]+(/[\w+#]+)*$", topic):
                    raise Exception("invalid topic")
                _mqtt_client.subscribe(topic)
                print(f"subscribed to {topic}")
                continue
            if cmd == "unsub":
                topic = input("topic: ")
                if not re.match(r"^\w+/[\w+#]+(/[\w+#]+)*$", topic):
                    raise Exception("invalid topic")
                _mqtt_client.unsubscribe(topic)
                print(f"unsubscribed to {topic}")
                continue
            print("unknown command")
        except Exception as e:
            print(f"{red_ascii}{str(e)}{reset}")


if __name__ == "__main__":
    prepare()
    _mqtt_client = mqtt.Client()
    _mqtt_client.on_connect = on_connect
    _mqtt_client.on_disconnect = on_disconnect
    _mqtt_client.on_message = on_message
    _mqtt_client.username_pw_set(_mqtt_username, _mqtt_password)
    _mqtt_client.connect(_mqtt_server, 1883, 60)
    _mqtt_client.loop_start()
    get_input()
    _mqtt_client.loop_stop()
    _mqtt_client.disconnect()
    logging.info("client stopped")
